李智慧 高并发架构实战课(三)
13 | 微博系统设计:怎么应对热点事件的突发访问压力?
Weitter 的技术挑战,一方面是微博这样类似的信息流系统架构是如何设计的,另 一方面就是如何解决大 V 们的热点消息产生的突发高并发访问压力,保障系统的可用性
需求分析
Weitter 的核心功能只有三个:发微博,关注好友,刷微博。
- 发微博:用户可以发表微博,内容包含不超过 140 个字的文本,可以包含图片和视频。
- 关注好友:用户可以关注其他用户。
- 刷微博:用户打开自己的微博主页,主页显示用户关注的好友最近发表的微博;用户向 下滑动页面(或者点刷新按钮),主页将更新关注好友的最新微博,且最新的微博显示 在最上方;主页一次显示 20 条微博,当用户滑动到主页底部后,继续向上滑动,会按 照时间顺序,显示当前页面后续的 20 条微博。
- 此外,用户还可以收藏、转发、评论微博。
性能指标估算
系统按 10 亿用户设计,按 20% 日活估计,大约有 2 亿日活用户(DAU),其中每个日活 用户每天发表一条微博,并且平均有 500 个关注者。
- 对于发微博所需的存储空间,我们做如下估算
- 文本内容存储空间
- 多媒体文件存储空间
- 对于刷微博的访问并发量,我们做如下估算
- QPS(高峰期 QPS 按平均值 2 倍计算)
- 网络带宽
概要设计
系统架构的核心就是解决高并发的问题,系统整体部署模型如下
详细设计
微博的发表 / 订阅问题
Weitter 最终采用“推拉结合”的模式。也就是说,如果用户当前在线,那么就会使用推模式,系统会在缓存中为其 创建一个好友最新发表微博列表,关注的好友如果有新发表微博,就立即将该微博插入列 表的头部,当该用户刷新微博的时候,只需要将这个列表返回即可。
如果用户当前不在线,那么系统就会将该列表删除。当用户登录刷新的时候,用拉模式为其重新构建列表。
那么如何确定一个用户是否在线?一方面可以通过用户操作时间间隔来判断,另一方面也可以通过机器学习,预测用户的上线时间,利用系统空闲时间,提前为其构建最新微博列表。
缓存使用策略
LRU 算法并不适合微博的场景
我们在 Weitter 中使用时间淘汰算法,也就是将最近一定天数内发布的微博全部 缓存起来,用户刷新微博的时候,只需要在缓存中进行查找。如果查找到的微博数满足一 次返回的条数(20 条),就直接返回给用户;如果缓存中的微博数不足,就再到数据库中查找。
对于特别热门的微博内容,针对单个微博内容的高并发访问,由于访问压力都集中一个缓存 key 上,会给单台 Redis 服务器造成极大的负载压力。因此,微博还会启用本地缓存模式,即应用服务器在内存中缓存特别热门的微博内容。
数据库分片策略
- 采用用户 ID 分片
- 好处:单用户微博都在一台实例,查找方便
- 缺点:明星大V或某用户频繁发微博,会导致这台服务器数据增长过快
- 采用微博 ID 分片
- 可以避免上述按用户 ID 分片的热点聚集问题
- 需要访问所有的分片数据库服务器才能得到所需的数 据,对数据库服务器集群的整体压力太大
最终,Weitter 采用按用户 ID 分片的策略。用户 ID 分片带来的热点问题,可以通过优化缓存来改善;而某个用户频繁发表 微博的问题,可以通过设置每天发表微博数上限(每个用户每天最多发表 50 条微博)来解决。
14 | 百科应用系统设计:机房被火烧了系统还能访问吗?
Wepedia 的功能比较简单,只有编辑词条和搜索查看词条这两个核心功能。设计目标主要是简单、高效地支持高并发访问,以及面对全球用户时保证 7 × 24 小时高可用。(支撑每日 10 亿次以上的访问压力)
概要设计
Wepedia 的整体架构,也就是简化的部署模型如图。
在梳理 Wepedia 整体逻辑之前,先说明下架构图中核心组件的作用。
具体的请求流程略。
Wepedia 为了解决 Nginx 缓存失效的问题,采用了另一种解决方案:失效通知。词条编 辑者修改词条后,Invalidation notification 模块就会通知所有 Nginx 服务器,该词条内 容失效,进而从缓存中删除它。
多数据中心架构
Wepedia 在全球部署多个数据中心,可以就近为用户提供服务。一是为了减少网络通信延迟,而是为了容灾备份。
Wepedia 的多数据中心架构如图。
用户请求是 Get 请求(读请求),那么请求就会在该数据中心处理。
如果请求是 Post 请求(写请求),那么请求到达 Nginx 的时候,Nginx 会判断并将该 Post 请求转发给主数据中心。
通过这种方式,主数据中心根据 Post 请求更新数据库后,再通过 Canal组件将更新同步 给其他所有从数据中心的 MySQL,从而使所有数据中心的数据保持一致。同样, LightHttp 中的图片数据也进行同步,开发 LightHttp 插件,将收到的图片,发送给所有 从数据中心。
数据中心之间采用类似 ZooKeeper 的选主策略进行通信,如果主数据中心不可用,其他 数据中心会重新选举一个主数据中心。而如果某个从数据中心失火了,用户请求域名解析 到其他数据中心即可。
这种多数据中心架构虽然使词条编辑操作的时间变长,但是由于 Wepedia 的绝大多数请 求都是 Get 请求(Get 与 Post 请求比超过 1000:1),因此对系统的整体影响并不很 大。同时用一种简单、廉价的方式实现多数据中心的数据一致性,开发和运维成本都比较低。
详细设计
详细设计重点关注 Wepedia 的性能优化。
前端性能优化
前端是指应用服务器(也就是 PHP 服务器)之前的部分,包括 DNS 服务、 CDN 服务、 反向代理服务、静态资源服务等。
Wepedia 前端架构的核心是反向代理服务器 Nginx 集群,大约需要部署数十台服务器。 请求通过 LVS 负载均衡地分发到每台 Nginx 服务器,热点词条被缓存在这里,大量请求可 直接返回响应,减轻应用负载压力。而 Nginx 缓存 不能命中的请求,会再通过 LVS 发送 到 Apache 应用服务器集群。
在反向代理 Nginx 之前,是 CDN 服务。
Wepedia CDN 缓存的几条准则:
- 内容页面不包含动态信息,以免页面内容缓存很快失效或者包含过时信息。
- 每个内容页面有唯一的 REST 风格的 URL,以便 CDN 快速查找并避免重复缓存。
- 在 HTML 响应头写入缓存控制信息,通过应用控制内容是否缓存及缓存有效期等。
服务端性能优化
Wepedia 需要将最好的服务器部署在这里(和数据库配置一样的服务 器),从硬件上改善性能。
除了硬件改善,Wepedia 还需要使用其他开源组件对应用层进行优化:
- 使用 APC,这是一个 PHP 字节码缓存模块,可以加速代码执行,减少资源消耗。
- 使用 Tex 进行文本格式化,特别是将科学公式内容转换成图片格式。
- 替换 PHP 的字符串查找函数 strtr(),使用更优化的算法重构。
存储端性能优化
存储端优化最主要的手段是使用缓存,将热点数据缓存在分布式缓存系统的内存中。
Wepedia 的缓存使用策略如下:
- 热点特别集中的数据直接缓存到应用服务器的本地内存中,因为要占用应用服务器的内 存且每台服务器都需要重复缓存这些数据,因此这些数据量很小,但是读取频率极高。
- 缓存数据的内容尽量是应用服务器可以直接使用的格式,比如 HTML 格式,以减少应用 服务器从缓存中获取数据后解析构造数据的代价。
- 使用缓存服务器存储 session 对象。
作为存储核心数据资产的 MySQL 数据库,需要做如下优化:
- 使用较大的服务器内存。在 Wepedia 应用场景中,增加内存比增加其他资源更能改善 MySQL 性能。
- 使用 RAID5 磁盘阵列以加速磁盘访问。
- 使用 MySQL 主主复制及主从复制,保证数据库写入高可用,并将读负载分散在多台服务器。
15 | 限流器设计:如何避免超预期的高并发压力压垮系统?
我们准备开发一个限流器,产品名称为“Diana”。
需求分析
我们将 Diana 定位为一个限流器组件,即 Diana 的主要应用场景是部署在微服务网关或者 其他 HTTP 服务器入口,以过滤器的方式对请求进行过滤,对超过限流规则的请求返 回“服务不可用”HTTP 响应。
Diana 的限流规则可通过配置文件获取,并需要支持本地配置和远程配置两种方式,远程 配置优先于本地配置。限流方式包括:
- 全局限流:针对所有请求进行限流,即保证整个系统处理的请求总数满足限流配置。
- 账号限流:针对账号进行限流,即对单个账号发送的请求进行限流。
- 设备限流:针对设备进行限流,即对单个客户端设备发送的请求进行限流。
- 资源限流:针对某个资源(即某个 URL)进行限流,即保证访问该资源的请求总数满足 限流配置。
并且 Diana 设计应遵循开闭原则,能够支持灵活的限流规则功能扩展,即未来在不修改现 有代码和兼容现有配置文件的情况下,支持新的配置规则。
概要设计
Diana 的设计目标是一个限流器组件,即 Diana 并不是一个独立的系统,不可以独立部署 进行限流,而是部署在系统网关(或者其他 HTTP 服务器上),作为网关的一个组件进行 限流,部署模型如下
限流器的策略可以在本地配置,也可以通过远程的配置中心服务器加载,即远程配置。远程配置优先于本地配置。
限流模式设计
本地记录称作本地限流,远程记录称作远程限流(也叫分布式限流)。
远程限流意味着,所有网关共享同一个限流数量。可能 某个网关服务器一段时间内根本就没有请求到达,但是远程的已处理请求数已经达到了限 流上限,那么这台网关服务器也必须拒绝请求。
我们使用 Redis 作为记录单位时间请求数 量的远程服务器。
高可用设计
限流器应具有自动降级功能,即配置中心不可用,则使用本地配置;Redis 服务器不可用,则降级为本地限流。
详细设计
- 限流器运行期需要通过配置文件获取对哪些 URL 路径进行限流;
- 本地限流还是分布式限流;
- 对用户限流还是对设备限流,还是对所有请求限流;
- 限流的阈值是多少;
- 阈值的时间单位是什么;
- 具体使用哪种限流算法。
- 常用的限流算法有 4 种,固定窗口(Window)限流算法,滑动窗口(Sliding Window) 限流算法,漏桶(Leaky Bucket)限流算法,令牌桶(Token Bucket)限流算法
配置文件设计
Diana 限流器使用 YAML 进行配置。配置文件的配置项有 7 种,分别说明如下:
- Url 记录限流的资源地址,”/“表示所有请求,配置文件中的路径可以互相包含,比 如“/”包含“/sample”,限流器要先匹配“/”的限流规则,如果“/”的限流规则还 没有触发(即访问”/“的流量,也就是单位时间所有的请求总和没有达到限流规则), 则再匹配“/sample”。
- 每个 Url 可以配置多个规则 rules,每个规则包括 actor,unit,rpu,algo,scope
- actor 为限流对象,可以是账号(actor),设备(device),全部(all)
- unit 为限流时间单位,可以是秒(second),分(minute),时(hour),天 (day)
- rpu 为单位时间限流请求数(request per unit),即上面 unit 定义的单位时间内允许 通过的请求数目,如 unit 为 second,rpu 为 100,表示每秒允许通过 100 个请求,每 秒超过 100 个请求就进行限流,返回 503 响应
- scope 为 rpu 生效范围,可以是本地(local),也可以是全局(global),scope 也 决定了单位时间请求数量是记录在本地还是远程,local 记录在本地,global 记录在远 程。
- algo 限流算法,可以是 window,sliding window,leaky bucket,token bucket 。
- 固定窗口(Window)限流算法
- 也是请求进入时才进行判断
- 滑动窗口(Sliding Window)限流算法
- 漏桶(Leaky Bucket)限流算法
- 使用队列这种方式,实际上是把请求处理异步化了。因此 Diana 实现漏桶限流算法并不使用消息队列,而是阻塞等待。注意,以上代码多线程并发执行,需要进行加锁操作。
- 使用漏桶限流算法,即使系统资源很空闲,多个请求同时到达时,漏桶也是慢慢地一个接一个地去处理请求,这其实并不符合人们的期望,因为这样就是在浪费计算资源。因此除非有特别的场景需求,否则不推荐使用该算法。
- 令牌桶(Token Bucket)限流算法
- 令牌桶的实现,只需要在请求获取令牌的时候,通过时间计算,就可以算出令牌桶中的总令牌数。【总令牌数 = min(令牌数上限值,总令牌数 +(now - 最近生成令牌时间戳) / 令牌生成时间间隔)】
- 建议通常场景中优先使用该算法,Diana 的缺省配置算法也是令牌桶。
16 | 高可用架构的十种武器:怎么度量系统的可用性?
我总结了一些高可用架构的技术方案,并称之为高可用架构的十种武器。
- 第一种武器:解耦
- 组件的低耦合原则:
- 无循环依赖原则:(即被依赖的组件尽量稳定,尽量少因为业务变化而变化)
- 稳定依赖原则:(组件就要更加抽象)
- 稳定抽象原则
- 面向对象的低耦合原则
- 开闭原则,即对修改封闭、对扩展开放;
- 依赖倒置原则,即高层对象不能依赖低层对象,而是要依赖抽象接口,而抽象接口属于高层;
* 接口隔离原则,不要强迫使用者依赖它们不需要的方法,要用接口对方法进行隔离
- 组件的低耦合原则:
- 第二种武器:隔离
隔离必须在低耦合的基础上进行才有意义。如果组件之间的耦合关
系千头万绪、混乱不堪,隔离只会让这种混乱更雪上加霜。 - 第三种武器:异步
使用消息队列的异步架构,新用户注册消息发送给消息队列就立即返回,后续的操作通过
消费消息来完成,即使某个操作发生故障也不会影响用户注册成功。 - 第四种武器:备份
备份与失效转移(failover)总是成对出现的,共同构成一个高可用解决方案。
最常见的备份就是负载均衡。多台服务器构成一个集群,这些服务器天然就是互相备份的关系,任何一台服务器失效,只需要将分发到这台服务器的请求分发给其他服务器即可 - 第五种武器:重试
可以重试的服务必须是幂等的。所谓幂等,即服务重复调用和调用一次产生的结果
是相同的。 - 第六种武器:熔断
熔断的主要方式是使用断路器阻断对故障服务器的调用,断路器状态图如下。 - 第七种武器:补偿
补偿则是故障发生后,如何弥补错误或者避免损失扩大。
补偿最典型的使用场景是事务补偿。
传统的事务回滚主要依赖数据库的特性,当事务失败的时候,数据库执行自己的 undo 日
志,就可以将同一个事务的多条数据记录恢复到事务之初的状态。但是分布式服务没有
undo 日志,所以需要开发专门的事务补偿代码,当分布式事务失效的时候,调用事务补偿
服务,将事务状态恢复如初。 - 第八种武器:限流
- 第九种武器:降级
解决办法就是在系统高并发的时候(例如淘宝双十一),将确认收货、评价这些非核心的
功能关闭,也就是对系统进行降级,把宝贵的系统资源留下来,给正在购物的人,让他们
去完成交易。 - 第十种武器:多活
异地多活的架构需要考虑的重点是,用户请求如何分发到不同的机房去。这个主要可以在
域名解析的时候完成,也就是用户进行域名解析的时候,会根据就近原则或者其他一些策
略,完成用户请求的分发。另一个至关重要的技术点是,因为是多个机房都可以独立对外
提供服务,所以也就意味着每个机房都要有完整的数据记录。用户在任何一个机房完成的
数据操作,都必须同步传输给其他的机房,进行数据实时同步。
数据库实时同步最需要关注的就是数据冲突问题。同一条数据,同时在两个数据中心被修
改了,该如何解决?某些容易引起数据冲突的服务采用类似 MySQL 的主主模式,也就是
说多个机房在某个时刻是有一个主机房的,某些请求只能到达主机房才能被处理,其他的
机房不处理这一类请求,以此来避免关键数据的冲突。
除了以上的高可用架构方案,还有一些高可用的运维方案。
- 通过自动化测试减少系统的 Bug。
- 通过自动化监控尽早发现系统的故障。
- 通过预发布验证发现测试环境无法发现的 Bug。
- 通过灰度发布降低软件错误带来的影响。
17 | Web 应用防火墙:怎样拦截恶意用户的非法请求?
我们准备开发一个 Web 应用防火墙,该防火墙可作为 Web 插件,部署在 Web 应用或者微服务网关等 HTTP 服务的入口,拦截恶意请求,保护系统 安全。我们准备开发的 Web 应用防火墙名称为“Zhurong(祝融)”。
需求分析
HTTP 请求发送到 Web 服务器时,请求首先到达 Zhurong 防火墙,防火墙判断请求中是 否包含恶意攻击信息。如果包含,防火墙根据配置策略,可选择拒绝请求,返回 418 状态 码;也可以将请求中的恶意数据进行消毒处理,也就是对恶意数据进行替换,或者插入某 些字符,从而使请求数据不再具有攻击性,然后再调用应用程序处理
概要设计
Zhurong 能够发现恶意攻击请求的主要手段,是对 HTTP 请求内容进行正则表达式匹配, 将各种攻击类型可能包含的恶意内容构造成正则表达式,然后对 HTTP 请求头和请求体进 行匹配。如果匹配成功,那么就触发相关的处理逻辑,直接拒绝请求;或者将请求中的恶 意内容进行消毒,即进行字符替换,使攻击无法生效。
其中,恶意内容正则表达式是通过远程配置来获取的。如果发现了新的攻击漏洞,远程配 置的漏洞攻击正则表达式就会进行更新,并在所有运行了 Zhurong 防火墙的服务器上生效,拦截新的攻击。组件图如下
漏洞规则定义文件采用 XML 格式
- recipe 是漏洞定义文件的根标签,属性 attacktype 表示处理的攻击类型
- ruleSet 是漏洞处理规则集合,一个漏洞文件可以包含多个 ruleSet。stage 标签表示处理的阶段;condition 表示和其他规则的逻辑关系
- action 表示发现攻击后的处理动作。
- rule 表示漏洞规则,触发漏洞规则,就会引起 action 处理动作
详细设计
XSS 跨站点脚本攻击
XSS 攻击即跨站点脚本攻击 (Cross Site Script),指黑客通过篡改网页,注入恶意 JavaScript 脚本,在用户浏览网页时,控制用户浏览器进行恶意操作的一种攻击方式。
常见的 XSS 攻击类型有两种,一种是反射型,攻击者诱使用户点击一个嵌入恶意脚本的链 接,达到攻击的目的。
另外一种 XSS 攻击是持久型 XSS 攻击,黑客提交含有恶意脚本的请求,保存在被攻击的 Web 站点的数据库中,用户浏览网页时,恶意脚本被包含在正常页面中,达到攻击的目的。
匹配成功后,根据漏洞定义文件,可以选择 forward 到错误页面,也可以采用 replace 方式进行消毒。
在 XSS 攻击字符前后加上“&nb sp;”(没空格)字符串,使得攻击脚本无法运行,同时在浏览器显 示的时候不会影响显示内容。
CSRF 跨站点请求伪造攻击
CSRF(Cross Site Request Forgery,跨站点请求伪造),攻击者通过跨站请求,以合法用 户的身份进行非法操作,如转账交易、发表评论等
主要手法是利用跨站请求,在用户不知情的情况下,以用户的身份伪造请求。其核 心是利用了浏览器 Cookie 或服务器 Session 策略,盗取用户身份。
Zhurong 的防攻击策略,是过滤器自动在所有响应页面的表单 form 中添加一个隐藏字 段,合法用户在提交请求的时候,会将这个隐藏字段发送到服务器,防火墙检查隐藏字段 值是否正确
注释与异常信息泄露
匹配 HTML 注释的正则表达式如下:“<!--(.|
|
)*-->”
如果匹配到 HTML 注释,就用空字符串 replace 该注释。
18 | 加解密服务平台:如何让敏感数据存储与传输更安全?
我们设计了一个加解密服务系统,系统名称为“Venus”,统一管理所有的加解密 算法和密钥。应用程序只需要依赖加解密服务 SDK,调用接口进行加解密即可,而真正的 算法和密钥在系统服务端进行管理,保证算法和密钥的安全。
需求分析
一般说来,日常开发中的加解密程序存在如下问题:
- 密钥(包括非对称加解密证书)保存在源文件或者配置文件中,存储分散而不安全。
- 密钥没有分片交换机制,不能满足高安全级密钥管理和交换的要求。
- 密钥缺乏版本管理,不能灵活升级,一旦修改密钥,此前加密的数据就可能无法解密。
- 加密解密算法程序不统一,同样算法不同实现,内部系统之间密文不能正确解析。
- 部分加解密算法程序使用了弱加解密算法和弱密钥,存在安全隐患。
Venus 是一个加解密服务系统,核心功能是加解密服务,辅助功能是密钥与算法管理。此外,Venus 还需要满足以下非功能需求:
- 安全性需求
- 必须保证密钥的安全性,保证没有人能够有机会看到完整的密钥。因此一个密钥至少要 拆分成两片,分别存储在两个异构的、物理隔离的存储服务器中 。在需要进行密钥交换 的场景中,将密钥至少拆分成两个片段,每个管理密钥的人只能看到一个密钥片段,需 要双方所有人分别交接才能完成一次密钥交换。
- 可靠性需求
- 加解密服务必须可靠,即保证高可用。无论在加解密服务系统服务器宕机、还是网络中 断等各种情况下,数据正常加解密都需要得到保障。
- 性能需求
- 加解密计算的时间延迟主要花费在加解密算法上,也就是说,加载加解密算法程序、获 取加解密密钥的时间必须短到可以忽略不计。
系统用例图设计如下:
- 加解密计算的时间延迟主要花费在加解密算法上,也就是说,加载加解密算法程序、获 取加解密密钥的时间必须短到可以忽略不计。
系统主要参与者(Actor)包括:
系统主要用例过程和功能包括:
- 开发工程师使用密钥管理功能为自己开发的应用申请加解密算法和密钥;
- 安全工程师使用密钥管理功能审核算法和密钥的强度是否满足数据安全要求;
- (经过授权的)密钥管理者使用密钥管理功能可以查看密钥(的一个分片);
- 应用程序调用加解密功能完成数据的加密、解密;
- 加密解密功能和密钥管理功能调用密钥服务功能完成密钥的存储和读取;
- 密钥服务功能访问一个安全、可靠的密钥存储系统读写密钥。
总地说来,Venus 应满足如下需求:
- 集中、分片密钥存储与管理,多存储备份,保证密钥安全易管理。
- 密钥申请者、密钥管理者、密钥访问者,多角色多权限管理,保证密钥管理与传递的安 全。
- 通过密钥管理控制台完成密钥申请、密钥管理、密钥访问控制等一系列密钥管理操作, 实现便捷的密钥管理。 4. 统一加解密服务 API,简单接口,统一算法,为内部系统提供一致的加解密算法实现。
概要设计
针对上述加解密服务及密钥安全管理的需求,设计加解密服务系统 Venus 整体结构如下:
部署模型
Venus 部署模型如图:
Venus 系统的核心服务器是 Key Server 服务器,提供密钥管理服务。密钥分片存储在文 件服务器 File Store 和数据库 DB 中。
加解密调用时序图
加解密调用过程如下时序图所示。
- 安全性:
- 对密钥进行分片存储,不同存储服务器由不同运维人员管理。
- 性能:
- 密钥缓存在 SDK 所在的进程,只有第一次调用时会访问远程的 Venus 服务器,加解密的性能只受加解密的数据大小和算法的影响
- 可靠性:
- 密钥在缓存中,如果 Venus 服务器临时宕机,或者网络通信中断,也不会影响 到应用程序的正常使用
- 服务器长时间宕机, 那么应用重新启动,本地缓存被清空,就需要重新请求密钥,这时候应用就不可用—>(解决方案)Venus 服务器、数据库和文件服务器做高可用备份。SDK 通过软负载均衡访问 Venus 服务器集群【失效转移、主从备份】
详细设计
密钥领域模型
核心服务类设计
以加密为例,具体处理过程时序图如下:
加解密数据接口 VenusData 设计
VenusData 用于表示 Venus 加解密操作输入和输出的数据,也就是说,加解密的时候构 造 VenusData 对象调用 Service 对应的方法,加解密完成后返回值还是一个 VenusData 对象。
VenusData 用作输入时:
- 属性 bytes 和 text 只要设置一个,即要么处理的是二进制 bytes 数据,要么是 Striing 数据,如果两个都设置了,Venus 会抛出异常。
- 属性 version 可以不设置(即 null),表示 Venus 操作使用的密钥版本是当前版本。
- 属性 outputWithText 表示输出的 VenusData 是否处理为 text 类型,缺省值是 true。
- 属性 dataWithVersion 表示加密后的 VenusData 的 bytes 和 text 中是否包含使用密 钥的版本信息,这样在解密的时候可以不指定版本,缺省值是 false。
如果 dataWithVersion 设置为 true,即表示加密后密文内包含版本号,这种情况下, VenusService 需要在密文头部增加 3 个字节的版本号信息,其中头两个字节为固定的 magic code:0x5E、0x23,第三个字节为版本号(也就是说,密钥版本号只占用一个字 节,最多支持 256 个版本)。
VenusData 用作输出时,Venus 会设置属性 keyName(和输入时的值一样)、 version、 bytes、 outputWithText、dataWithVersion(和输入时的值一样),并根据 输入的 outputWithText 决定是否设置 text 属性。